/**
 *  @file: DataTask.cpp
 *  @author: nwz
 *  @brief: 
 *  @date: 2024��7��31��
 * 
 */
#define TAG "datatask"
#include <code/task/DataTask.h>
#include "all_header.h"

#define ABORT()		ASSERT(0);

struct {
	uint8_t header_identify[4] = { 0xAA, 0xAA, 0xAA, 0xAA };
	uint8_t brightness = DEFAULT_BRIGHTNESS;
	uint8_t reserved[100];
} __attribute__ ((aligned (16))) flash_data;

#define FLASH_MAP_ADDR				0x08000000
#define DATA_FLASH_SIZE				sizeof(flash_data)
#define DATA_FLASH_START			(1*1024*1024 - 1*FLASH_SECTOR_SIZE)
#undef FLASH_BANK_SIZE  /// 有时读取时会报错
#define FLASH_BANK_SIZE				(1024*1024)

static int get_bank(uint32_t start) {
	if (start >= FLASH_BANK_SIZE)
		return FLASH_BANK_2;
	return FLASH_BANK_1;
}

static int get_sector(uint32_t start) {
	if (start >= FLASH_BANK_SIZE) {
		return (start - FLASH_BANK_SIZE) / FLASH_SECTOR_SIZE;
	}
	return start / FLASH_SECTOR_SIZE;
}

static void erase_data() {
	uint32_t SectorError;
	FLASH_EraseInitTypeDef EraseInitStruct;
	MEM_ZERO(EraseInitStruct);

	EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
	EraseInitStruct.Banks = get_bank(DATA_FLASH_START);
	EraseInitStruct.Sector = get_sector(DATA_FLASH_START);
	EraseInitStruct.NbSectors = 1;

	if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
		ABORT();
	}
}

static void get_data() {
	memcpy(&flash_data, (void*) (FLASH_MAP_ADDR + DATA_FLASH_START),
	DATA_FLASH_SIZE);
}

static void save_data() {
	auto size = 0;
	while (size < DATA_FLASH_SIZE) {
		if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD,
		FLASH_MAP_ADDR + DATA_FLASH_START + size, (uint32_t) &flash_data + size)
				!= HAL_OK) {
			ABORT();
		}
		size += 16;
	}
}

namespace task {

DataTask::DataTask() {
	// TODO Auto-generated constructor stub
	if (DATA_FLASH_SIZE % 16) {
		ABORT();
	}
	if (DATA_FLASH_SIZE > FLASH_SECTOR_SIZE) {
		ABORT();
	}
}

DataTask::~DataTask() {
	// TODO Auto-generated destructor stub
}

void DataTask::setBacklight(uint8_t brightness) {
	if (flash_data.brightness != brightness) {
		flash_data.brightness = brightness;
		notify();
	}
}

uint8_t DataTask::getBacklight() {
	return flash_data.brightness;
}

int DataTask::init() {
	sem = xSemaphoreCreateBinary();
	if (!sem) {
		ABORT();
	}

	if (*(uint32_t*) (FLASH_MAP_ADDR + DATA_FLASH_START) != 0xAAAAAAAA) {
		HAL_ICACHE_Disable();
		ASSERT(HAL_FLASH_Unlock() == HAL_OK);
		erase_data();
		save_data();
		ASSERT(HAL_FLASH_Lock() == HAL_OK);
		HAL_ICACHE_Enable();
	}

	get_data();
	return 0;
}

void DataTask::wait() {
	xSemaphoreTake(sem, portMAX_DELAY);
}

void DataTask::notify() {
	xSemaphoreGive(sem);
}

void DataTask::run(DataTask *task) {

	while (true) {
		task->wait();
		HAL_ICACHE_Disable();
		HAL_FLASH_Unlock();
		erase_data();
		save_data();
		HAL_FLASH_Lock();
		HAL_ICACHE_Enable();

		get_data();
	}
}

} /* namespace task */
